#ifndef _MY_SERVICE_
#define _MY_SERVICE_
#include "data.hpp"
#include"httplib.h"
namespace cloud
{
   extern cloud::DataManager* _data;
    class Service
    {
        private:
            int _server_port;
            string _server_ip;
            string _download_prefix;
            httplib::Server _server;
        private:
           static void Upload(const httplib::Request& req,httplib::Response & rsp)    //上传文件
            {
                // post /upload 文件数据在正文中
                auto ret=req.has_file("file");
                if(ret==false)
                {
                    rsp.status=400;
                    return;
                }
                const auto& file=req.get_file_value("file");
                //file.filename//文件名称   file.content//文件数据
                string back_dir=Config::GetInstance()->GetBackDir();
                string realpath=back_dir+FileUtil(file.filename).FileName();
                FileUtil fu(realpath);
                fu.SetContent(file.content); //将数据写入文件中
                BackupInfo info;
                info.NewBackupInfo(realpath);   //组织备份的文件信息
                _data->Insert(info);    //向数据管理模块添加备份的文件信息
            }
            string TimetoStr(time_t t)
            {
                string tmp=ctime(&t);
                return tmp;
            }
            static void ListShow(const httplib::Request &req, httplib::Response &rsp)   //为什么要设置为static属性，因为在类内的各种方法默认第一个参数是this，因此要加上static使其方法只包含两个参数
            {
                // 1.获取所有的文件备份信息
                vector<BackupInfo> arry;
                _data->GetAll(&arry);
                // 2.根据所有备份信息，组织html文件
                std::stringstream ss;
               ss<<"<html><head><title>Download</title></head>";
               ss<<"<body><h1>Download</h1><table>";
               for(auto&a:arry)
               {
                ss<<"<tr>";
                string filename=FileUtil(a.real_path).FileName();
                ss<<"<td><a href='"<<a.url<<"'>"<<filename<<"</a></td>";
                ss<<"<td align='right'>"<<TimetoStr(a.mtime)<<"</td>";
                ss<<"<td align='right'>"<<a.fsize/1024<<"k</td>";
                ss<<"</tr>";
               }
                ss<<"</table></body></html>";
                rsp.body = ss.str();
                rsp.set_header("Content-Type", "text/html");
                rsp.body = 200;
                return;
            }
            static string GetETag(const BackupInfo & info)  //GetETag要被放入set_header函数的第二个参数中就必须设置为静态的
            {
                //etg: filename-fsize-mtime
                FileUtil fu(info.real_path);
                string etag=fu.FileName();
                etag+="-";
                etag+=std::to_string(info.fsize);
                etag+="-";
                etag+=std::to_string(info.mtime);
                return etag;
            }
            static void Download(const httplib::Request &req, httplib::Response &rsp)
            {
                //1.获取客户端请求的资源路径path req.path
                //2.根据资源路径，获取文件备份信息
                BackupInfo info;
                _data->GetOneByURL(req.path,&info);
                //3.判断文件是否被压缩，如果被压缩要先解压缩
                if(info.pack_flag==true)
                {
                    FileUtil fu(info.pack_path);
                    fu.UnCompress(info.real_path); // 将文件解压到备份目录下
                    // 4.删除压缩包，修改备份信息(已经没有被压缩了)
                    fu.Remove();
                    info.pack_flag = false;
                    _data->Update(info);
                }
                bool retrans = false; // 标识是否进行断点续传
                string old_etag;
                if (req.has_header("If-Range"))
                {
                    old_etag = req.get_header_value("If-Range");
                    // 判断前后etag值是否一致，一致则用断点续传
                    if (old_etag == GetETag(info))
                    {
                        retrans = true;
                    }
                }
                // 如果没有If-Range字段则正常下载，或者如果有此字段或者发现
                // 它的值与当前文件的etag不一致则也要重新返回全部数据
                // 4.读取文件数据，放入rsp.body中
                FileUtil fu(info.real_path);
                // 5.设置响应头部字段: ETag，Accept-Ranges: bytes
                rsp.set_header("Accept-Ranges", "bytes");
                rsp.set_header("ETag", GetETag(info));
                rsp.set_header("Content-Type", "application/octet-stream"); //设置Content-Type字段表示处理数据的方式
                if (retrans == false)
                {
                    rsp.status = 200;
                }
                else
                {
                    rsp.status = 206;
                }
                return;
            }

        public:
            Service()
            {
                Config *config = Config::GetInstance();
                _server_port = config->GetServerPort();
                _server_ip = config->GetServerIp();
                _download_prefix = config->GetDownloadPrefix();
            }
            bool RunModule()
            {
                _server.Post("/upload",Upload); //Post和Get函数的第二个参数是一个参数有2个的回调方法
                _server..Get("/listshow",ListShow);
                _server.Get("/",ListShow);
                string download_url=_download_prefix+"(.*)";
                _server.Get(download_url,Download);
                _server.listen(_server_ip.c_str(),_server_port);
                return true;
            }
    }       
}

#endif